package stud.mpgame.snapshot;
import stud.mpgame.flash.test.TestCommand;
import stud.mpgame.network.NetworkMonitor;
import stud.mpgame.network.service.time.TimeService;
import stud.mpgame.flash.test.TestCommandDescription;
import stud.mpgame.network.Message;


class SnapshotManager {
	
	private var localSnapshot:ISnapshot;
	private var nextServerSnapshots:List< ISnapshot >;
	private var commands:List< ICommand >;
	private var nextApplyCommandTime:Int;
	private var serverTickTime:Int;
	private var temporarySnapshot:ISnapshot;
	private var newestSnapshotTime:Int;
	private var timeService:TimeService;
	
	private var networkMonitor:NetworkMonitor;
	
	public function new(serverTickTime:Int, localSnapshot:ISnapshot, timeService:TimeService, networkMonitor:NetworkMonitor) {
		this.serverTickTime = serverTickTime;
		this.localSnapshot = localSnapshot;
		this.timeService = timeService; 
		this.newestSnapshotTime = 0;
		nextServerSnapshots = new List< ISnapshot >();
		commands = new List< ICommand >();
		nextApplyCommandTime = localSnapshot.getTime() + serverTickTime;
		
		this.networkMonitor = networkMonitor;
	}
	
	public function addServerSnapshot(serverSnapshot:ISnapshot):Void {
		var serverSnapshotTime:Int = serverSnapshot.getTime();
		if (serverSnapshotTime <= newestSnapshotTime) {
			return;
		}
		newestSnapshotTime = serverSnapshotTime;
		if (serverSnapshotTime <= localSnapshot.getTime()) {
			localSnapshot.synchronize(serverSnapshot);
			temporarySnapshot = getTemporarySnapshot(localSnapshot.getTime(), serverSnapshot);
		} else {
			temporarySnapshot = null;
			nextServerSnapshots.add(serverSnapshot);
		}
	}
	
	public function addCommand(command:ICommand):Void {
		commands.add(command);
	}
	
	public function getLocalSnapshotTime():Int {
		return localSnapshot.getTime();
	}
	
	public function update(deltaTime:Int):ISnapshot {
		if ( deltaTime <= 0 )
			return localSnapshot;
		
		var needLocalSnapshotTime:Int = localSnapshot.getTime() + deltaTime;
		if (nextServerSnapshots.isEmpty()) {
			if (temporarySnapshot == null || temporarySnapshot.getTime() <= needLocalSnapshotTime)
				temporarySnapshot = null;
			if(temporarySnapshot == null) {
				localSnapshot.extrapolate(deltaTime);
			}
			else
				localSnapshot.interpolate(deltaTime, temporarySnapshot);
		} else {
			var destinationServerSnapshot:ISnapshot = null;
			var previousServerSnapshot:ISnapshot = null;
			for (serverSnapshot in nextServerSnapshots) {
				if (serverSnapshot.getTime() >= needLocalSnapshotTime) {
					if (previousServerSnapshot != null) {
						localSnapshot.synchronize(previousServerSnapshot);
						nextServerSnapshots.remove(previousServerSnapshot);
					}
					destinationServerSnapshot = serverSnapshot;
					break;
				} else {
					if (previousServerSnapshot != null)
						nextServerSnapshots.remove(previousServerSnapshot);
						
					//GreK
					previousServerSnapshot = serverSnapshot;
				}
			}
			if (destinationServerSnapshot == null) {
				//GreK: Зачем??? Достаточно просто extrapolate.
				var lastServerSnapshot:ISnapshot = nextServerSnapshots.last();
				nextServerSnapshots.clear();
				temporarySnapshot = getTemporarySnapshot(needLocalSnapshotTime, lastServerSnapshot);
				destinationServerSnapshot = temporarySnapshot;
			}
			localSnapshot.interpolate(deltaTime, destinationServerSnapshot);
		}
		//if (timeService.getServerTime() >= nextApplyCommandTime) {
		if ( localSnapshot.getTime() >= nextApplyCommandTime ) {
			for (command in commands)
			{
				var tCmd:TestCommand = cast(command, TestCommand);
				tCmd.setTime(nextApplyCommandTime);
				trace("Apply command: " + nextApplyCommandTime);
				command.apply(localSnapshot); //remove 
				
				var message:Message = new Message(networkMonitor, TestCommandDescription.getInstance());
				message.putUserObject(tCmd);
				message.send();	
			}
		
			//GreK
			commands.clear();
			var num = Std.int( (localSnapshot.getTime() - nextApplyCommandTime) / serverTickTime ) + 1;
			nextApplyCommandTime += num * serverTickTime;
		}
		return localSnapshot;
	}
	
	private function getTemporarySnapshot(nowTime:Int, serverSnapshot:ISnapshot):ISnapshot {
		var deltaTime:Int = (Std.int((nowTime - serverSnapshot.getTime()) / serverTickTime) + 1) * serverTickTime;
		serverSnapshot.extrapolate(deltaTime);
		return serverSnapshot;
	}
}